File Encryption and Decryption in Swift using AES Algorithm

Data security is of utmost importance when dealing with sensitive information. Encryption plays a vital role in safeguarding data from unauthorized access. In this technical blog, we will explore how to encrypt and decrypt files in Swift using the Advanced Encryption Standard (AES) algorithm. We will provide a Swift implementation that demonstrates the encryption and decryption process using a symmetric key.

Final project

This tutorial allow you to create a simple macOS status bar application (with registered file handle: “Open with…”) for crypt and encrypt file, instantly and easily:

AES Encryption and Decryption:

AES is a widely used encryption algorithm that provides a strong level of security. It operates on fixed-size blocks of data and supports key sizes of 128, 192, and 256 bits. AES uses symmetric encryption, meaning the same key is used for both encryption and decryption.

Implementation:

Let’s dive into the implementation of file encryption and decryption in Swift using the AES algorithm.

Step 1: Setting Up the Project

Start by creating a new Swift project in Xcode. Choose the appropriate project template based on your requirements.

Step 2: Importing Required Frameworks

To use AES encryption, we need to import the necessary frameworks. In the project navigator, select your project, go to the “Build Phases” tab, and add the “Security.framework” framework.

Additionally, we need to import the CommonCrypto module.

Step 3.1: String Encryption

To encrypt a file, first of all you need to encrypt his content, so you can save in a new file:

private func encrypt(data: Data, key: String) -> Data? {
let keyData = key.data(using: .utf8)!
let inputData = data as NSData
let encryptedData = NSMutableData(length: Int(inputData.length) + kCCBlockSizeAES128)!
let keyLength = size_t(kCCKeySizeAES128)
let operation = CCOperation(kCCEncrypt)
let algorithm = CCAlgorithm(kCCAlgorithmAES)
let options = CCOptions(kCCOptionPKCS7Padding)

var numBytesEncrypted: size_t = 0

let cryptStatus = CCCrypt(
operation,
algorithm,
options,
(keyData as NSData).bytes, keyLength,
nil,
inputData.bytes, inputData.length,
encryptedData.mutableBytes, encryptedData.length,
&numBytesEncrypted
)

if cryptStatus == kCCSuccess {
encryptedData.length = Int(numBytesEncrypted)
return encryptedData as Data
}

return nil
}

this function returns an encrypted Data object.

Step 3.2: File Encryption

Once you have the new Data object, you can save it to a new file:

func encryptFile(atPath filePath: String, withKey key: String) throws {
let fileURL = URL(fileURLWithPath: filePath)
let fileData = try Data(contentsOf: fileURL)

guard let encryptedData = encrypt(data: fileData, key: key) else {
throw NSError(domain: "EncryptionError", code: -1, userInfo: nil)
}

let encryptedFile = "\(filePath).psk"
try encryptedData.write(to: URL(filePath: encryptedFile), options: .atomic)
print("File encrypted successfully.")
}

Step 4.1: String Decryption

Same for encryption, decryption must pass from Data and getting back the string value.

private func decrypt(data: Data, key: String) -> Data? {
let keyData = key.data(using: .utf8)!
let inputData = data as NSData
let decryptedData = NSMutableData(length: Int(inputData.length) + kCCBlockSizeAES128)!
let keyLength = size_t(kCCKeySizeAES128)
let operation = CCOperation(kCCDecrypt)
let algorithm = CCAlgorithm(kCCAlgorithmAES)
let options = CCOptions(kCCOptionPKCS7Padding)

var numBytesDecrypted: size_t = 0

let cryptStatus = CCCrypt(
operation,
algorithm,
options,
(keyData as NSData).bytes, keyLength,
nil,
inputData.bytes, inputData.length,
decryptedData.mutableBytes, decryptedData.length,
&numBytesDecrypted
)

if cryptStatus == kCCSuccess {
decryptedData.length = Int(numBytesDecrypted)
return decryptedData as Data
}

return nil
}

Step 4.2: File Decryption

The new file, can be saved:

func decryptFile(atPath filePath: String, withKey key: String) throws {
let fileURL = URL(fileURLWithPath: filePath)
let fileData = try Data(contentsOf: fileURL)

guard let decryptedData = decrypt(data: fileData, key: key) else {
throw NSError(domain: "DecryptionError", code: -1, userInfo: nil)
}

let decryptedFile = filePath.replacingOccurrences(of: ".psk", with: "")
try decryptedData.write(to: URL(filePath: decryptedFile), options: .atomic)
print("File decrypted successfully.")
}

Register the file-type

Open your .plist file and need to add all the extensions you want to use to make your file to be opened with this new tool:

In this way, you can easily “Open with” directly on your app:

Handle file opening:

When a file is opened, you need to handle it when selected. To do this, add in your AppDelegate or SceneDelegate, the file handler:

class AppDelegate: NSObject, NSApplicationDelegate {
private let fileHandler = FileHandler()
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSApplication.shared.delegate = fileHandler
}
}

class FileHandler: NSObject, NSApplicationDelegate {
func application(_ sender: NSApplication, openFile filename: String) -> Bool {
NotificationCenter.default.post(name: NSNotification.Name("kFileOpened"), object: filename)
return true
}
}

Next, in your ViewController, implement the notification listener:

override func viewDidLoad() {
super.viewDidLoad()

NotificationCenter.default.addObserver(
self,
selector: #selector(didFileOpened),
name: NSNotification.Name("kFileOpened"),
object: nil
)
}

@objc func didFileOpened( _ notification: Notification ) {
if let fileName = notification.object as? String {
print( fileName )
}
}

This is the core of the app, the macOS UI of course, is at your own…

Conclusion:

We learned how to encrypt and decrypt files in Swift using the AES algorithm. We explored the implementation of file encryption and decryption, along with the necessary AES encryption logic. By leveraging the power of AES encryption, you can secure your sensitive files and protect them from unauthorized access.

You can now backup your files, or share a super secret file without worrying about security.

Enjoy encrypt!